<template>
	<div>
		<div ref="vtkContainer" class="content" />
		<table class="controls">
			<tbody>
				<tr>
					<td>
						<select style="width: 100%" :value="representation" @change="setRepresentation">
							<option value="0">Points</option>
							<option value="1">Wireframe</option>
							<option value="2">Surface</option>
						</select>
					</td>
				</tr>
				<tr>
					<td>
						<select style="width: 100%;margin-top: 10px;margin-bottom: 10px;" :value="color" @change="setColor">
							<option value="0">X</option>
							<option value="1">Y</option>
							<option value="2">Z</option>
						</select>
					</td>
				</tr>
				<tr>
					最大色值
				</tr>
				<tr>
					<td>
						<input type="range" min="0" max="1" step="0.001" :value="colorClamp" @input="setColorClamp" />
					</td>
				</tr>
				<tr>
					<td>
						<div v-if="loadproc < 99.999">模型加载中： {{ loadproc + '%' }} ***MB</div>
					</td>
				</tr>
				<tr>
					<td>
						<input type="file" @change="inputChange" />
					</td>
				</tr>
			</tbody>
		</table>
	</div>
</template>

<script setup lang="ts">
import { ref, unref, onMounted, onBeforeUnmount, watchEffect } from 'vue';

import '@kitware/vtk.js/Rendering/Profiles/All';

import vtkFullScreenRenderWindow from '@kitware/vtk.js/Rendering/Misc/FullScreenRenderWindow';

import vtkActor from '@kitware/vtk.js/Rendering/Core/Actor';
import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper';
import vtkCalculator from '@kitware/vtk.js/Filters/General/Calculator';
import { FieldDataTypes } from '@kitware/vtk.js/Common/DataModel/DataSet/Constants';
import vtkSTLReader from '@kitware/vtk.js/IO/Geometry/STLReader';
import vtkObjReader from '@kitware/vtk.js/IO/Misc/ObjReader';
// Load the itk-wasm UMD module dynamically for the example.
// Normally, this will just go in the HTML <head>.
import vtkResourceLoader from '@kitware/vtk.js/IO/Core/ResourceLoader';
import vtkScalarBarActor from '@kitware/vtk.js/Rendering/Core/ScalarBarActor';
// import { AttributeTypes } from '@kitware/vtk.js/Common/DataModel/DataSetAttributes';
import vtkPointPicker from '@kitware/vtk.js/Rendering/Core/PointPicker';
import vtkPolyData from '@kitware/vtk.js/Common/DataModel/PolyData';
import * as d3 from 'd3';
import vtkSphereSource from '@kitware/vtk.js/Filters/Sources/SphereSource';
import { readMeshArrayBuffer, readFile } from 'itk-wasm'

import { TeGeo } from '/@/utils/eTool';
const vtkContainer = ref();
const representation = ref(2);
const color = ref(2);
const colorClamp = ref(1);
const loadproc = ref(0)
const context = ref()

const actors: vtkActor[] = []
const actors_: vtkActor[] = []
const filters: vtkCalculator[] = []

//@ts-ignore
const getItk = () => window.itk

function setRepresentation(rep) {
	representation.value = Number(rep.target.value);
}
function setColor(c) {
	color.value = Number(c.target.value);
}
function setColorClamp(c) {
	colorClamp.value = Number(c.target.value);
	if (!context.value) return
	if (loadproc.value < 99.999) return
	const { updateColClamp } = context.value;
	updateColClamp()
}

watchEffect(() => {
	if (!context.value) return
	if (loadproc.value < 99.999) return
	const rep = unref(representation);
	for (const actor of actors) {
		actor.getProperty().setRepresentation(rep);
	}
	context.value?.update()
});

const inputChange = (e: any) => {
	for (const actor of actors) {
		context.value?.picker.deletePickList(actor)
		actor.getMapper()?.delete()
		actor.delete()
	}
	for (const actor of actors_) {
		actor.getMapper()?.delete()
		actor.delete()
	}
	console.log(e)
	const file = e.target.files[0]
	loadMesh(file.name, file)
}

function generateTicks(numberOfTicks) {
	return (helper) => {
		const lastTickBounds = helper.getLastTickBounds();
		// compute tick marks for axes
		const scale = d3
			.scaleLinear()
			.domain([0.0, 1.0])
			.range([lastTickBounds[0], lastTickBounds[1]]);
		const samples = scale.ticks(numberOfTicks);
		const ticks = samples.map((tick) => scale(tick));
		// Replace minus "\u2212" with hyphen-minus "\u002D" so that parseFloat() works
		// d3.formatDefaultLocale({ minus: '\u002D' } as any);
		const format = scale.tickFormat(
			ticks[0],
			ticks[ticks.length - 1] as any
		);
		const tickStrings = ticks
			.map(format)
			.map((tick) => Number(parseFloat(tick).toPrecision(12)).toPrecision()); // d3 sometimes adds unwanted whitespace
		helper.setTicks(ticks);
		helper.setTickStrings(tickStrings);
	};
}

function updateColClamp() {
	for (const actor of actors) {
		actor.getMapper()?.setScalarRange(0, colorClamp.value);
	}
	context.value?.update()
}

const updateFilter = (filter: vtkCalculator) => {
	filter.setFormula({
		getArrays: _inputDataSets => ({
			input: [{ location: FieldDataTypes.COORDINATE }],
			output: [
				{ location: FieldDataTypes.POINT, name: 'Random', dataType: 'Float32Array', attribute: 0 }
			]
		}),
		evaluate: (_arraysln: any, arraysOut: any) => {
			const [coords] = _arraysln.map(d => d.getData());
			const [scalars] = arraysOut.map(d => d.getData())
			// console.log(scalars)
			const addI = color.value
			for (let i = 0; i < scalars.length; i++) {
				scalars[i] = coords[i * 3 + addI] / 20
			}
		}
	})
}

const createMesh = (pd: any) => {
	const filter = vtkCalculator.newInstance()
	const mapper = vtkMapper.newInstance();
	let lut = mapper.getLookupTable();
	context.value?.scalarBarActor.setScalarsToColors(lut);
	mapper.setInputConnection(filter.getOutputPort());
	const actor = vtkActor.newInstance();
	actor.setMapper(mapper);
	updateFilter(filter)
	filter.setInputData(pd)
	actors.push(actor)
	filters.push(filter)
	context.value?.renderer.addActor(actor)
	context.value?.picker.addPickList(actor);
	return actor
}

const loadMesh = (path: string, file?: any) => {
	if (!path) return
	const _path = path.substring(path.lastIndexOf('.') + 1, path.length)
	switch (_path) {
		case 'stl': return loadStl(path, file);
		case 'STL': return loadStl(path, file);
		case 'obj': return loadObj(path, file);
		case 'OBJ': return loadObj(path, file);
		case 'vtu': return loadVtu(path, file);
	}
}
const loadStl = (path: string, file?: any) => {
	if (file) {
		loadproc.value = 100
		let fr = new FileReader()
		fr.onload = _ => {
			console.log(fr.result)
			const reader = vtkSTLReader.newInstance();
			reader.parseAsArrayBuffer(fr.result as any)
			createMesh(reader.getOutputData())
			context.value?.update()
		}
		fr.readAsArrayBuffer(file)
		return
	}
	const rq = new XMLHttpRequest()
	rq.open('GET', path, true)
	rq.responseType = 'arraybuffer'
	rq.onprogress = (e) => {
		loadproc.value = Math.round(e.loaded / (e.total || 15677634) * 10000) / 100
	}
	rq.onloadend = async () => {

		const result = await readMeshArrayBuffer(
			null,
			rq.response,
			'slz.stl',
			''
		);
		console.log(result)

		// const reader = vtkSTLReader.newInstance();
		// reader.parseAsArrayBuffer(rq.response)
		// reader.getOutputPort()
		// const outdata = reader.getOutputData()
		// const pd = outdata.getPoints().getData()
		// const pd1 = outdata.getPolys().getData()
		// const egeo = TeGeo.parseData(pd, pd1, 100000)
		// for (const _egeo of egeo.allChildren) {
		// 	const pl = vtkPolyData.newInstance()
		// 	pl.getPoints().setData(_egeo.points_, 3)
		// 	pl.getPolys().setData(_egeo.indices_)
		// 	createMesh(pl)
		// }
		// console.log(pd, pd1, egeo, outdata.toJSON())
		// // createMesh(reader.getOutputData())
		// context.value?.update()
	}
	rq.send()
}
const loadObj = (path: string, file?: any) => {

	if (file) {
		readFile(
			null,
			file
		).then(result => {
			console.log(result)
		})

		// const reader = vtkObjReader.newInstance();
		// loadproc.value = 100
		// let fr = new FileReader()
		// fr.onload = _ => {
		// 	reader.parseAsText(fr.result as any)
		// 	const size = reader.getNumberOfOutputPorts();
		// 	for (let i = 0; i < size; i++) {
		// 		const polydata = reader.getOutputData(i);
		// 		createMesh(polydata)
		// 	}
		// 	context.value?.update()
		// }
		// fr.readAsText(file)
		return
	}
	return
	const rq = new XMLHttpRequest()
	rq.open('GET', path, true)
	rq.responseType = 'arraybuffer' //'text'
	rq.onprogress = (e) => {
		loadproc.value = Math.round(e.loaded / (e.total || 15677634) * 10000) / 100
	}
	rq.onloadend = async () => {
		// reader.parseAsText(rq.responseText)
		// const size = reader.getNumberOfOutputPorts();
		// for (let i = 0; i < size; i++) {
		// 	const polydata = reader.getOutputData(i);
		// 	createMesh(polydata)
		// }
		// context.value?.update()
		const { mesh, webWorker } = await readMeshArrayBuffer(
			null,
			rq.response,
			'B_L_1_Lower.obj',
			''
		);
		const { polyData } = await getItk().meshToPolyData(
			webWorker,
			mesh
		);
		console.log(mesh, webWorker, polyData)
	}
	rq.send()
}
const loadVtu = async (path: string, file?: any) => {

	if (file) {
		// const result = await readMeshArrayBuffer(null, file)
		// console.log(result)
		// d59ff68bf062eed0acf9e28bbe2cfb68_5aca69e52e1e4fd59f6fd06230ebaf9d_8.vtu
		return
	}
	const rq = new XMLHttpRequest()
	rq.open('GET', path, true)
	rq.responseType = 'arraybuffer'
	rq.onprogress = (e) => {
		loadproc.value = Math.round(e.loaded / (e.total || 15677634) * 10000) / 100
	}
	rq.onloadend = async () => {
		const result = await readMeshArrayBuffer(
			null,
			rq.response,
			'd59ff68bf062eed0acf9e28bbe2cfb68_5aca69e52e1e4fd59f6fd06230ebaf9d_8.vtu',
			''
		);
		console.log(result)
		// const { polyData } = await getItk().meshToPolyData(
		// 	webWorker,
		// 	mesh
		// );
		// console.log(mesh, webWorker, polyData)
	}
	rq.send()
}

const init_vtk = () => {
	if (context.value) return

	const onlyWebGPU = window.location.href.includes('onlyWebGPU')
	let useWebGpu = true
	//@ts-ignore
	if (!navigator.gpu) {
		// alert('WebGPU is not enabled. ' + (onlyWebGPU ? '' : 'used WebGL!'));
		useWebGpu = onlyWebGPU || false
		if (onlyWebGPU) return
	}
	const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance({
		container: vtkContainer.value,
		defaultViewAPI: useWebGpu ? 'WebGPU' : ''
	} as any);

	const renderer = fullScreenRenderer.getRenderer();
	const renderWindow = fullScreenRenderer.getRenderWindow();
	if (useWebGpu) renderWindow.setDefaultViewAPI('WebGPU' as any)

	const picker = vtkPointPicker.newInstance();
	picker.setPickFromList(1);
	picker.initializePickList();


	// 颜色线
	const scalarBarActor = vtkScalarBarActor.newInstance();
	scalarBarActor.setAxisLabel('颜色值')
	scalarBarActor.setGenerateTicks(generateTicks(10));
	renderer.addActor(scalarBarActor)
	// loadMesh('B_L_1_Lower.stl') //B_L_1_Lower slz
	// loadMesh('d59ff68bf062eed0acf9e28bbe2cfb68_5aca69e52e1e4fd59f6fd06230ebaf9d_8.vtu')
	loadMesh('slz.stl')
	context.value = {
		fullScreenRenderer,
		renderWindow,
		scalarBarActor,
		renderer,
		picker,
		update,
		updateColClamp
	};
	// renderWindow.getInteractor().onLeftButtonPress((callData) => {
	// 	// console.log(callData)
	// 	if (renderer !== callData.pokedRenderer) {
	// 		return;
	// 	}

	// 	const pos = callData.position;
	// 	const point = [pos.x, pos.y, 0.0];
	// 	picker.pick(point, renderer);
	// 	const pickedPointId = picker.getPointId();
	// 	const pickedPoints = picker.getPickedPositions();
	// 	console.log('Picked point: ', pickedPointId, pickedPoints);
	// 	const sphere = vtkSphereSource.newInstance();
	// 	sphere.setCenter(pickedPoints[0] as any);
	// 	sphere.setRadius(1);
	// 	const sphereMapper = vtkMapper.newInstance();
	// 	sphereMapper.setInputData(sphere.getOutputData());
	// 	const sphereActor = vtkActor.newInstance();
	// 	sphereActor.setMapper(sphereMapper);
	// 	sphereActor.getProperty().setColor(1.0, 0.0, 0.0);
	// 	renderer.addActor(sphereActor);
	// 	actors_.push(sphereActor)
	// 	update()
	// })

	function update() {
		renderer.resetCamera();
		renderWindow.render();
		// console.log(renderWindow.toJSON())
	}
}

const init = () => {
	// vtkResourceLoader
	// 	.loadScript(
	// 		'https://cdn.jsdelivr.net/npm/itk-wasm@1.0.0-b.70/dist/umd/itk-wasm.min.js'
	// 		// 'loader/itk-wasm.min.js'
	// 	)
	// 	.then(() => {
	// 		init_vtk()
	// 	});
	init_vtk()
}




onMounted(() => {
	init()

});

onBeforeUnmount(() => {
	if (!context.value) return
	const { fullScreenRenderer } = context.value;
	fullScreenRenderer.delete();
	context.value = null;
});
</script>

<style scoped lang="scss">
.content {
	width: 100vw;
	height: 100vh;
}

.controls {
	position: absolute;
	top: 25px;
	left: 25px;
	background: white;
	padding: 12px;
}
</style>
